﻿package src {
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.CapsStyle;
	import flash.display.Graphics;
	import flash.display.LineScaleMode;
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.TextField;
	import flash.text.TextFormat;
	
	/**
	 * To represent one row of sequence data
	 * @author Indrek Tamming
	 */
	public class SequenceRow {
		
		/**
		 * Sequence name
		 */
		private var name:String;
		
		/**
		 * Sequence as String
		 */
		private var seq:String;
		
		/**
		 * Secuence row constructor
		 * @param	name	-	sequence name
		 * @param	seq		-	sequence as String
		 */
		public function SequenceRow(name:String, seq:String):void {
			this.name = name;
			this.seq = seq;
		}
		
		/**
		 * Draw the squence row and tracks
		 * @param	leftSymbolIndex
		 * @param	zoomLevel
		 * @param	conservationRows
		 * @param	conservationParams
		 * @param	lettersInRow
		 * @param	letterSize
		 * @param	viewWidth
		 * @param	letters
		 * @return	a Sprite with sequence row drawn on
		 */
		public function draw(leftSymbolIndex:int, zoomLevel:int, conservationRows:Array, conservationParams:Array, lettersInRow:int, letterSize:Number, viewWidth:Number, letters:BitmapData):Sprite {
			var sprite:Sprite = new Sprite();
			var g:Graphics = sprite.graphics;
			
			// Add conservation:
			for (var j:int = 0; j < conservationRows.length; j++) {
				var conservationRow:Array = conservationRows[j];
				if (conservationRow != null && conservationRow.length > 0) {
					var conservation:Sprite = drawConservation(conservationRow, zoomLevel, leftSymbolIndex, conservationParams[j], letterSize, lettersInRow);
					sprite.addChild(conservation);
				}
			}
			
			// Draw seq:
			var _x:Number = 0;
			var seqArray:Array = seq.split("");
			var maxI:int = leftSymbolIndex + lettersInRow;
			if (zoomLevel > 2) {
				g.moveTo(0, 0);
				g.lineStyle(1);
				var showEndpoint:Boolean = false;
				if (leftSymbolIndex >= seq.length - lettersInRow) showEndpoint = true;
				if (showEndpoint) {
					_x = letterSize * seq.length - leftSymbolIndex;
					g.lineTo(_x, 0);
					g.lineStyle(2, 0xFF0000);
					g.lineTo(_x, -3);
					g.lineTo(_x, 3);
				}
				else g.lineTo(viewWidth, 0);
				return sprite;
			}
			
			// If letters ACGT are visible:
			var lettersBmpData:BitmapData = new BitmapData(750, letterSize, true, 0);
			var point:Point = new Point(0, 0);
			var rect:Rectangle = new Rectangle(0, zoomLevel == 1 ? 0 : 10, letterSize, letterSize);
			for (var i:int = leftSymbolIndex; i <= maxI; i++) {
				if (i < 0 || seqArray[i] == null) {
					_x += letterSize;
					continue;
				}
				if (zoomLevel == 1) {
					switch(seqArray[i]) {
						case "A": rect.x = 0; break;
						case "C": rect.x = 10; break;
						case "G": rect.x = 20; break;
						case "T": rect.x = 30; break;
						default: break;
					}
				}
				else if (zoomLevel == 2) {
					switch(seqArray[i]) {
						case "A": rect.x = 0; break;
						case "C": rect.x = 5;  break;
						case "G": rect.x = 10; break;
						case "T": rect.x = 15; break;
						default: break;
					}
				}
				lettersBmpData.copyPixels(letters, rect, point);
				_x += letterSize;
				point.x = _x;
				if (i == maxI - 1 && leftSymbolIndex >= seq.length - lettersInRow) {
					// The endpoint:
					g.lineStyle(2, 0xFF0000);
					g.moveTo(_x + 2, -letterSize/2);
					g.lineTo(_x + 2, letterSize*1.5);
				}
			}
			sprite.addChild(new Bitmap(lettersBmpData, "auto", true));
			return sprite;
		}
		
		/**
		 * Draw sequence name
		 * @param	zoomLevel
		 * @return
		 */
		public function drawName(zoomLevel:int):Sprite {
			var sprite = new Sprite();
			
			var nameField:TextField = new TextField();
			nameField.y -= zoomLevel <= 2 ? 20 : 15;
			nameField.selectable = false;
			var nameFieldFormat:TextFormat = new TextFormat();
			if (zoomLevel <= 2) nameFieldFormat.size = 11;
			else if (zoomLevel == 3) nameFieldFormat.size = 9;
			else nameFieldFormat.size = 8;
			nameFieldFormat.font = "Arial";
			nameField.defaultTextFormat = nameFieldFormat;
			nameField.text = name;
			sprite.addChild(nameField);
			
			return sprite;
		}
		
		/**
		 * Draw conservation
		 * @param	conservationRow
		 * @param	zoomLevel
		 * @param	leftSymbolIndex
		 * @param	conservationParams
		 * @param	letterSize
		 * @param	lettersInRow
		 * @return
		 */
		public function drawConservation(conservationRow:Array, zoomLevel:int, leftSymbolIndex:int, conservationParams:Object, letterSize:Number, lettersInRow:int):Sprite {
			if (Boolean(conservationParams.hidden)) return new Sprite();
			
			if (conservationParams.style == "center") {
				return drawCenterConservation(conservationRow, zoomLevel, leftSymbolIndex, conservationParams, letterSize, lettersInRow);
			}
			else if (conservationParams.style == "upper") {
				return drawUpperConservation(conservationRow, zoomLevel, leftSymbolIndex, conservationParams, letterSize, lettersInRow);
			}
			else if (conservationParams.style == "lower") {
				return drawLowerConservation(conservationRow, zoomLevel, leftSymbolIndex, conservationParams, letterSize, lettersInRow);
			}
			else {
				return new Sprite();
			}
		}
		
		/**
		 * Draw "center" conservation
		 * @param	conservationRow
		 * @param	zoomLevel
		 * @param	leftSymbolIndex
		 * @param	conservationParams
		 * @param	letterSize
		 * @param	lettersInRow
		 * @return
		 */
		private function drawCenterConservation(conservationRow:Array, zoomLevel:int, leftSymbolIndex:int, conservationParams:Object, letterSize:Number, lettersInRow:int):Sprite {
			var s:Sprite = new Sprite();
			var g:Graphics = s.graphics;
			var _x:Number = 0;
			
			var heightMultiplier:Number = 14;
			var maxI:int = leftSymbolIndex + lettersInRow;
			var _height:Number;
			var _alpha:Number;
			for (var i:int = leftSymbolIndex; i < maxI; i++) {
				if (conservationRow[i] == null || i < 0) {
					_x += letterSize;
					continue;
				}
				_height = (conservationRow[i] * heightMultiplier);
				g.beginFill(conservationParams.color, conservationRow[i]);
				g.drawRect(_x, (-1) * _height, letterSize, 2 * _height);
				g.endFill();
				_x += letterSize;
			}
			if (zoomLevel <= 2) s.y += letterSize * 0.5;
			return s;
		}
		
		/**
		 * Draw "upper" conservation
		 * @param	conservationRow
		 * @param	zoomLevel
		 * @param	leftSymbolIndex
		 * @param	conservationParams
		 * @param	letterSize
		 * @param	lettersInRow
		 * @return
		 */
		private function drawUpperConservation(conservationRow:Array, zoomLevel:int, leftSymbolIndex:int, conservationParams:Object, letterSize:Number, lettersInRow:int):Sprite {
			var s:Sprite = new Sprite();
			var g:Graphics = s.graphics;
			var _x:Number = 0;
			
			var heightMultiplier:Number = 14;
			var maxI:int = leftSymbolIndex + lettersInRow;
			var _height:Number;
			var _alpha:Number;
			for (var i:int = leftSymbolIndex; i < maxI; i++) {
				if (conservationRow[i] == null || i < 0 || conservationRow[i] <= 0) {
					_x += letterSize;
					continue;
				}
				_height = (conservationRow[i] * heightMultiplier);
				g.beginFill(conservationParams.color, conservationRow[i] * 0.33);
				g.drawRect(_x, (-1) * (_height+letterSize), letterSize, _height);
				g.endFill();
				g.lineStyle(1, conservationParams.color, 1, true, "normal", CapsStyle.ROUND);
				g.moveTo(_x, ( -1) * (_height + letterSize));
				g.lineTo(_x + letterSize, ( -1) * (_height + letterSize));
				g.lineStyle();
				_x += letterSize;
			}
			if (zoomLevel <= 2) s.y += letterSize * 0.5;
			return s;
		}
		
		/**
		 * Draw "lower" conservation
		 * @param	conservationRow
		 * @param	zoomLevel
		 * @param	leftSymbolIndex
		 * @param	conservationParams
		 * @param	letterSize
		 * @param	lettersInRow
		 * @return
		 */
		private function drawLowerConservation(conservationRow:Array, zoomLevel:int, leftSymbolIndex:int, conservationParams:Object, letterSize:Number, lettersInRow:int):Sprite {
			var s:Sprite = new Sprite();
			var g:Graphics = s.graphics;
			var _x:Number = 0;
			
			var heightMultiplier:Number = 14;
			var maxI:int = leftSymbolIndex + lettersInRow;
			var _height:Number;
			var _alpha:Number;
			for (var i:int = leftSymbolIndex; i < maxI; i++) {
				if (conservationRow[i] == null || i < 0 || conservationRow[i] <= 0) {
					_x += letterSize;
					continue;
				}
				_height = (conservationRow[i] * heightMultiplier);
				g.beginFill(conservationParams.color, conservationRow[i] * 0.33);
				g.drawRect(_x, letterSize, letterSize, _height);
				g.endFill();
				g.lineStyle(1, conservationParams.color, 1, true, "normal", CapsStyle.ROUND);
				g.moveTo(_x, letterSize + _height);
				g.lineTo(_x + letterSize, letterSize + _height);
				g.lineStyle();
				_x += letterSize;
			}
			if (zoomLevel <= 2) s.y += letterSize * 0.5;
			return s;
		}
		
		/**
		 * Get sequence as String
		 * @return
		 */
		public function getSeq():String {
			return seq;
		}
		
		/**
		 * Get name as String
		 * @return
		 */
		public function getName():String {
			return name;
		}
		
	}
	
}